#include <math.h>
#include <InformationManager.h>
#include <UnitGroupManager.h>
#include "ThreatMap.h"
#include "Vector.h"
using namespace BWAPI;

ThreatMap* TheThreatMap = NULL;

// Returns damage multiplier applied to damage of the given type to units of the given size
double damageMultiplier(DamageType type,UnitSizeType size)
{
  if (type == DamageTypes::Concussive && size == UnitSizeTypes::Large) return 0.25;
  if (type == DamageTypes::Concussive && size == UnitSizeTypes::Medium) return 0.5;
  if (type == DamageTypes::Concussive && size == UnitSizeTypes::Small) return 1.0;
  if (type == DamageTypes::Explosive && size == UnitSizeTypes::Large) return 1.0;
  if (type == DamageTypes::Explosive && size == UnitSizeTypes::Medium) return 0.75;
  if (type == DamageTypes::Explosive && size == UnitSizeTypes::Small) return 0.5;
  if (type == DamageTypes::Normal && size == UnitSizeTypes::Large) return 1.0;
  if (type == DamageTypes::Normal && size == UnitSizeTypes::Medium) return 1.0;
  if (type == DamageTypes::Normal && size == UnitSizeTypes::Small) return 1.0;
  return 0.0;
}

ThreatMap::ThreatMap()
{
  TheThreatMap = this;
}

ThreatMap* ThreatMap::create()
{
  if (TheThreatMap) return TheThreatMap;
  return new ThreatMap();
}

void ThreatMap::destroy()
{
  if (TheThreatMap)
    delete TheThreatMap;
}

Position* ThreatMap::calculateMove(Unit* unit,std::list<Potential*> potentials)
{
  Vector netForce(0,0);
  Vector unitPos(unit->getPosition().x(),unit->getPosition().y());
  // Calculate gradient from enemy units
  for each (Unit* e in TheInformationManager->getAllKnownEnemy()) {
    UnitType type = TheInformationManager->getType(e);
    WeaponType e_weapon = type.groundWeapon();
    if (unit->getType().isFlyer()) {
      e_weapon = type.airWeapon();
    }
    double K = e_weapon.damageAmount()*damageMultiplier(e_weapon.damageType(),unit->getType().size());
    if (K == 0) continue;
    Position pos = TheInformationManager->getLastPosition(e);
    Potential p(K,e_weapon.maxRange(),&pos);
    Vector force = p.calcForce(&(unit->getPosition()));
    netForce = netForce + force;

    Broodwar->drawCircle(CoordinateType::Map,pos.x(),pos.y(),e_weapon.maxRange(),Colors::Red,false);
  }
  // Calculate gradient from provided list of additional potentials
  for each (Potential* p in potentials) {
    Vector force = p->calcForce(&(unit->getPosition()));
    netForce = netForce + force;
  }
  double netForce_len_sq = netForce.lengthSq(); // the squared length of the gradient vector
  if (netForce_len_sq == 0) return &(unit->getPosition()); // Don't move
  double netForce_len = sqrt(netForce_len_sq);
  // adjust gradient to ensure movement by sufficient distance
  // NOTE: may cause issues for land units near obstacles (ex. Move Zergling off cliff, zergling runs around)
  netForce.scale(36/netForce_len);
  return new Position((int)(unitPos.x + netForce.x),(int)(unitPos.y + netForce.y));
}
